// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.


/*

    File Replication Sample
    Server System Service

    FILE: FileRepServerProc.cpp
    
    PURPOSE: Remote procedures for server system service
    
    FUNCTIONS:

    COMMENTS:

*/

#include "common.h"

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <tchar.h>
#include <string.h>
#include <time.h>

// header file generated by MIDL compiler
#include "FileRepServer.h"

// Declarations for service routines.
#include "Service.h"

#ifdef DEBUG2
#include "DbgMsg.h"
#endif

extern HANDLE ServerCompletionPort;
extern LONG nThreadsAtServerCompletionPort;

#define ServerCompletionPortTimeout (20*1000)


typedef enum tSReqState {
    StateArrived,
    StateQueued,
    StateActive
} SReqState;

typedef enum {
  IoFileRep,
  IoPipe,
  IoFile
} IoCompletionType;

typedef enum {
  Activate,
  Read,
  Push,
  Wait,
} ActionType;

#ifdef DEBUG1
unsigned nServerReqs = 0;
#endif

//
// Packages up the variables to be passed
// to the processing thread.
//
typedef struct tSReq{

    handle_t hFileRepClient;
    HANDLE hTokenHandle;

    HANDLE hLocalFile;

    LPTSTR LocalFileName;

    // Set when we are impersonating the client.
    BOOL bImpersonating;

    // Priority of this request.
    UINT Pri;

    // User SID.
    PSID pSID;

    SReqState State;

    // The out-pipe we use to pull data from the server.
    ASYNC_CHAR_PIPE_TYPE *OutPipe;

    // The async handle for the call to RemoteOpen.
    RPC_ASYNC_STATE *Async;

    BYTE pbReadBuf[PULL_BUFSIZE];
  
    LONG FileReadPos;
    ULONG PushSize;

    OVERLAPPED FileOl;

    ActionType Action;

    CRITICAL_SECTION Lock;

    BOOL bPushOutstanding;
    BOOL bReadOutstanding;
    BOOL bBuf;

    ULONG cbRead;

    // Set when all the non-null pushes have been dispatched.
    // This happens after reading the last segment of the file
    // and calling a push for it.  What remains to be done
    // is waiting for notification for the last data push and issuing a
    // NULL push.
    BOOL bLastDataPushIssued;

    // Set when all of the file reads have completed.
    BOOL bReadsDone;

    // Set when issuing the last push of 0 length.
    BOOL bNullPush;

    // Set when a pull fails and RPC runtime cleans up the call.
    // There is no need to call Cancel after that.
    BOOL fCallInvalidated;

    // We use this wariable to set an error code so that
    // ServerShutdownRequest can know whether an error has occurred.
    // If no error occurred, the async call will be completed.
    // Otherwise it will be cancelled.
    DWORD Status;

} SReq;


//
// Handles the requests placed on req queue and active req queue
// and returns when no requests are available.
//
SReq* FindSReq(void) {

    SReq *pReq = NULL;

#ifdef DEBUG2
    TCHAR Msg[MSG_SIZE];
    ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
    int nCharWritten; 
#endif

    //
    // We will now go and check if the request or the active request sets of queues have anything that we
    // can service.  If they do not, then this thread can terminate.
    //

    // Check if the request queues have anything on them.  If they do,
    // pick a request off the highest priority queue and handle it, but only if we
    // are not handling enough of those requests already.
    for (UINT pri = NumPriGroups; pri > 0; pri--) {
      pReq = (SReq *) QueueRemove(ServerReqQueues[pri-1]);
      if (pReq != NULL) {
        break;
      }
    }

    if (pReq != NULL) {

#ifdef DEBUG2
      nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Took req %p off Req queue %p\n"), pReq, ServerReqQueues[pReq->Pri]);
      bufSize -= nCharWritten;
      DbgMsgRecord(Msg);
#endif
      
      if (CounterIncrement(pServerActiveReqCounters[pReq->Pri])) {
        if (QueueHashIncrementCounter(ServerActiveReqHashCounters[pReq->Pri], pReq->pSID)) {
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Incremented ServerActiveReqCounters[%d] and ServerActiveReqHashCounters[%d]\n"), pReq->Pri, pReq->Pri);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif
          // The request now resides on a new queue.
          CounterDecrement(pServerReqCounters[pReq->Pri]);
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ServerReqCounters[%d]\n"), pReq->Pri);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif            
          pReq->State = StateActive;
          
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Handling active req %p\n"), pReq);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif
          return pReq;

        }
        else {
          // There were too many requests for a given SID, place the request back
          // onto the queue.
          
          // Don't forget to decrement the counter for the group!
          CounterDecrement(pServerActiveReqCounters[pReq->Pri]);

          QueueAdd(ServerReqQueues[pReq->Pri], pReq, TRUE);
          
#ifdef DEBUG2
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Too many active requests for a SID\n"));
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
          nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Put req %p onto Req queue %p\n"), pReq, ServerReqQueues[pReq->Pri]);
          bufSize -= nCharWritten;
          DbgMsgRecord(Msg);
#endif
        }
      }
      else {
        // There were too many active requests for the user group, place the request back
        // onto the queue.
        
        QueueAdd(ServerReqQueues[pReq->Pri], pReq, TRUE);

#ifdef DEBUG2
        nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Too many active requests for priority %d\n"), pReq->Pri);
        bufSize -= nCharWritten;
        DbgMsgRecord(Msg);
        nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Put req %p onto Req queue %p\n"), pReq, ServerReqQueues[pReq->Pri]);
        bufSize -= nCharWritten;
        DbgMsgRecord(Msg);
#endif
      }
    }

    return NULL;
}

VOID FindAndActivateSReq(VOID) {
    DWORD status;

    tSReq *pReq = FindSReq();

    if (pReq) {

#ifdef DEBUG1
      QueueAdd(ServerActiveReqQueue, pReq, TRUE);
#endif

      // All that is requeired to activate the request is to queue a completion
      // packet with Activate key and the Req as Overlapped.
      status = PostQueuedCompletionStatus(ServerCompletionPort,
                                          0,
                                          IoFileRep,
                                          (LPOVERLAPPED)pReq);
      ASSERT(status);
    }
}

//
// Closes the file handle and frees all thread data if an error occured
// in one of the following functions.
//
VOID ServerShutdownRequest(SReq *pReq) {
    DWORD status;
    RPC_STATUS rpcstatus;


#ifdef DEBUG2
    TCHAR Msg[MSG_SIZE];
    ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
    int nCharWritten; 
    DbgMsgRecord(TEXT("-> ServerShutdownRequest\n"));
#endif

    ASSERT(pReq != NULL);

#ifdef DEBUG1
nServerReqs--;
#endif


    // We are actually deleting a request, pick a new one from the queue.
    FindAndActivateSReq();

    // Stop impersonating if we are.
    // The first thing we do in handling a request is impersonating the client.
    if (pReq->bImpersonating) {
      rpcstatus = RpcRevertToSelf();
      if (rpcstatus != RPC_S_OK) {
        AddToMessageLogProcFailureEEInfo(TEXT("ServerShutdownRequest: RpcRevertToSelf"), rpcstatus);
      }
    }
    
    // Complete or cancel the async call, depending on whether an error
    // has occurred.
    if (pReq->Status && !pReq->fCallInvalidated) {
      RpcAsyncAbortCall(pReq->Async,
                        TRUE);
    }
    else {
      RpcAsyncCompleteCall(pReq->Async,
                           &status);
    }
    
    // Check that hLocalFile has been initialized and that initialization
    // was successful.
    if (pReq->hLocalFile != NULL && pReq->hLocalFile != INVALID_HANDLE_VALUE) {
      status = CloseHandle(pReq->hLocalFile);
      ASSERT(status != 0);
    }

    if (pReq->LocalFileName != NULL) {
      AutoHeapFree(pReq->LocalFileName);
    }
    
    // Check if any of the counters need to be decremented
    // since we are removing the request.
    if (pReq->State == StateQueued) {
      CounterDecrement(pServerReqCounters[pReq->Pri]);
#ifdef DEBUG2
      nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ServerReqCounters[%d]\n"), pReq->Pri);
      bufSize -= nCharWritten;
      DbgMsgRecord(Msg);
#endif            
    }
    if (pReq->State == StateActive) {
      CounterDecrement(pServerActiveReqCounters[pReq->Pri]);
      QueueHashDecrementCounter(ServerActiveReqHashCounters[pReq->Pri], pReq->pSID);
#ifdef DEBUG2
      nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Decremented ServerActiveReqCounters[%d] and ServerActiveReqHashCounters[%d]\n"), pReq->Pri, pReq->Pri);
      bufSize -= nCharWritten;
      DbgMsgRecord(Msg);
#endif

#ifdef DEBUG1
      QueueRemoveData(ServerActiveReqQueue, pReq);
#endif
    }
    
    if (pReq->hTokenHandle != NULL) {
      status = CloseHandle(pReq->hTokenHandle);
      ASSERT(status != NULL);
    }
    
    if (pReq->pSID != NULL) {
      AutoHeapFree(pReq->pSID);
    }
    
    if (pReq->Lock.DebugInfo) {
      DeleteCriticalSection(&pReq->Lock);
    }
    
    AutoHeapFree(pReq);

#ifdef DEBUG2
    DbgMsgRecord(TEXT("<- ServerShutdownRequest\n"));
#endif
}

//
// Handles a request taken off req queue.
// Returns TRUE on sucess.
//
BOOL HandleSReq(tSReq *pReq) {

#ifndef NO_SEC
    // We need to impersonate the user that has issued the
    // remote call.
    // We want to impersonate as soon as possible to minimise
    // the amount of resources that can be consumed by an attack.
    if (ImpersonateLoggedOnUser(pReq->hTokenHandle) == 0) {      
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("HandleReq: ImpersonateLoggedOnUser"), GetLastError());
        return FALSE;
    }
    pReq->bImpersonating = TRUE;
#endif

    // Attempt to open the local file.
    if ((pReq->hLocalFile = CreateFile(pReq->LocalFileName,
                            GENERIC_READ,
                            FILE_SHARE_READ,
                            NULL,
                            OPEN_EXISTING,
                            FILE_FLAG_OVERLAPPED,
                            NULL)) == INVALID_HANDLE_VALUE) {
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("HandleReq: CreateFile"), GetLastError());
        return FALSE;
    }

#ifndef NO_SEC
    // We need to stop impersonating before putting this request back into
    // a queue or quitting.
    if (RevertToSelf() == 0) {
        AddToMessageLogProcFailure(TEXT("HandleReq: RevertToSelf"), GetLastError());
    }
    pReq->bImpersonating = FALSE;
#endif

    // Initialize the request fileds for throtteling the IO

    pReq->FileOl.Offset = 0;
    pReq->FileOl.OffsetHigh = 0;
    pReq->FileOl.hEvent = NULL;

    pReq->Async->u.IOC.dwCompletionKey = IoPipe;
    pReq->Async->u.IOC.lpOverlapped = (LPOVERLAPPED) pReq;

    pReq->bLastDataPushIssued = FALSE;
    pReq->bReadsDone = FALSE;
    pReq->bNullPush = FALSE;

    pReq->Async->Flags = RPC_C_NOTIFY_ON_SEND_COMPLETE;
    pReq->Async->UserInfo = pReq;
    pReq->Async->NotificationType = RpcNotificationTypeIoc;
    pReq->Async->u.IOC.hIOPort = ServerCompletionPort;

    // Link this file to the client requests's IO completion port:
    if ((ServerCompletionPort = CreateIoCompletionPort (pReq->hLocalFile,
                                                        ServerCompletionPort,
                                                        IoFile,
                                                        0)) == NULL) {
        AddToMessageLogProcFailure(TEXT("HandleActiveReq: CreateIoCompletionPort"), GetLastError());
        return FALSE;
    }

    pReq->bReadOutstanding = FALSE;
    pReq->bPushOutstanding = FALSE;
    pReq->bBuf = FALSE;

    // Init the critsec.
    if (InitializeCriticalSectionAndSpinCount(&pReq->Lock, 10) == 0) {
        AddToMessageLogProcFailure(TEXT("HandleActiveReq: InitializeCriticalSectionAndSpinCount"), GetLastError());
        return FALSE;
    }

    return TRUE;
}

//
// Handles a request taken off active req queue
//
VOID SServiceRequests(VOID) {
    tSReq *pReq;

#ifdef DEBUG2
    TCHAR Msg[MSG_SIZE];
    ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
    int nCharWritten; 
#endif

    DWORD dwNumberOfBytesTransferred;
    DWORD dwCompletionKey;
    OVERLAPPED *lpOverlapped;

    ActionType Action = Wait;

    while(TRUE) {

      //
      // Read data from file.
      //
      if (Action == Read) {

        EnterCriticalSection(&pReq->Lock);

        ASSERT(!pReq->bBuf);
        ASSERT(!pReq->bReadOutstanding);


        // We did not read anything yet.
        pReq->cbRead = 0;

        pReq->bReadOutstanding = TRUE;

#ifdef DEBUG2
        nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: reading file for req %p\n"), GetCurrentThreadId(), pReq);
        bufSize -= nCharWritten;
        DbgMsgRecord(Msg);
#endif

        if(!ReadFile(pReq->hLocalFile, pReq->pbReadBuf, PUSH_BUFSIZE, &pReq->cbRead, &pReq->FileOl)) {
          if (GetLastError() != ERROR_IO_PENDING) {
            AddToMessageLogProcFailure(TEXT("HandleActiveSReq: WriteFile"), GetLastError());
            pReq->Status = GetLastError();

            LeaveCriticalSection(&pReq->Lock);
            
            ServerShutdownRequest(pReq);

            Action = Wait;
          }
        }

        Action = Wait;

        LeaveCriticalSection(&pReq->Lock);
      }

      //
      // Push the data to the client.
      //
      else if (Action == Push) {

        EnterCriticalSection(&pReq->Lock);

#ifdef DEBUG2
        nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: pushing a buffer for req %p\n"), GetCurrentThreadId(), pReq);
        bufSize -= nCharWritten;
        DbgMsgRecord(Msg);
#endif

        // Doing a regular Push for the data read from the file.
        if (!pReq->bNullPush) {
          ASSERT(!pReq->bReadOutstanding);
          ASSERT(!pReq->bPushOutstanding);
          ASSERT(pReq->bBuf);
          
          pReq->bPushOutstanding = TRUE;
          

          pReq->Status = ((WINAPI_MY_PIPE_PUSH) pReq->OutPipe->push)(pReq->OutPipe->state, (char *)pReq->pbReadBuf, pReq->cbRead);
          
          pReq->bBuf = FALSE;
          
          if(pReq->Status != RPC_S_OK) {
            pReq->bReadsDone = TRUE;
            pReq->bPushOutstanding = FALSE;
            pReq->fCallInvalidated = TRUE;

            if (pReq->bReadOutstanding == FALSE) {
              LeaveCriticalSection(&pReq->Lock);
              ServerShutdownRequest(pReq);
              
              Action = Wait;
            }
            else
              LeaveCriticalSection(&pReq->Lock);
          }
          else {

            if (!pReq->bReadsDone)
              Action = Read;
            else {
              Action = Wait;
              pReq->bLastDataPushIssued = TRUE;
            }

            LeaveCriticalSection(&pReq->Lock);
          }
        }

        // Doing a "NULL" push to signal the end of the pipe out-stream.
        else {
          ASSERT(!pReq->bReadOutstanding);
          ASSERT(!pReq->bPushOutstanding);
          ASSERT(!pReq->bBuf);
          
          pReq->bPushOutstanding = TRUE;

          
          WINAPI_MY_PIPE_PUSH pPush = (WINAPI_MY_PIPE_PUSH) pReq->OutPipe->push;
          
          pReq->Status = pPush(pReq->OutPipe->state, NULL, 0);

          if(pReq->Status != RPC_S_OK) {
            pReq->fCallInvalidated = TRUE;
            LeaveCriticalSection(&pReq->Lock);
            ServerShutdownRequest(pReq);
              
            Action = Wait;
            }
          else {
            Action = Wait;
            LeaveCriticalSection(&pReq->Lock);  
          }
        }
      }

      //
      // Wait on the completion port.
      //
      else if (Action == Wait) {

        InterlockedIncrement(&nThreadsAtServerCompletionPort);

        ASSERT(nThreadsAtServerCompletionPort >= 0);

#ifdef DEBUG2
        nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: waiting on a completion port\n"), GetCurrentThreadId());
        bufSize -= nCharWritten;
        DbgMsgRecord(Msg);
#endif

        if (!GetQueuedCompletionStatus(ServerCompletionPort,
                                       &dwNumberOfBytesTransferred,
                                       (PULONG_PTR)&dwCompletionKey,
                                       &lpOverlapped,
                                       ServerCompletionPortTimeout)) {

          LONG nThreads = InterlockedDecrement(&nThreadsAtServerCompletionPort);
          
          if (GetLastError() == WAIT_TIMEOUT) {


#ifdef DEBUG2
            nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: timed out waiting on a completion port\n"), GetCurrentThreadId());
            bufSize -= nCharWritten;
            DbgMsgRecord(Msg);
#endif

            if (nThreads != 0) {
              return;
            }
            else {
              continue;
            }
          }
          else {
            AddToMessageLogProcFailure(TEXT("SServiceRequests: GetQueuedCompletionStatus"), GetLastError());
            
            Action = Wait;
          }
        }

        else {

          // If this is the last thread to come off a completion port,
          // spin up an extra worker thread.
          if (InterlockedDecrement(&nThreadsAtServerCompletionPort) == 0) {
            HANDLE hThread;
            ULONG ThreadIdentifier;
            DWORD status;
            
            if ((hThread = CreateThread(NULL,
                                        0,
                                        (LPTHREAD_START_ROUTINE) SServiceRequests,
                                        NULL,
                                        0,
                                        &ThreadIdentifier)) == NULL) {

              // If creating a thread fails there is really nothing that we
              // we can do.  Just continue.
              AddToMessageLogProcFailure(TEXT("SServiceRequests: CreateThread"), GetLastError());
            }
            else {
              // Unless we close a handle to the thread, it will remain in the
              // system even after its execution has terminated.
              status = CloseHandle(hThread);
              ASSERT(status != 0);  
            }
          }
          
          ASSERT(nThreadsAtServerCompletionPort >= 0);
          

          //
          // Received file IO completion.
          //
          if (dwCompletionKey == IoFile) {
            
            pReq = (SReq *) ((size_t)lpOverlapped - offsetof(SReq, FileOl));

#ifdef DEBUG2
            nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: completed File IO for req %p\n"), GetCurrentThreadId(), pReq);
            bufSize -= nCharWritten;
            DbgMsgRecord(Msg);
#endif
            
            EnterCriticalSection(&pReq->Lock);


            pReq->cbRead = dwNumberOfBytesTransferred;
            

            ASSERT(pReq->bReadOutstanding);
            ASSERT(!pReq->bBuf);
            
            pReq->bReadOutstanding = FALSE;
            pReq->bBuf = TRUE;
            
            ASSERT(!pReq->bLastDataPushIssued);
            
            if(pReq->Status != RPC_S_OK) {
              LeaveCriticalSection(&pReq->Lock);
              ServerShutdownRequest(pReq);
              
              Action = Wait;
            }
            else {
              
              // If we read less then we asked, then EOF has been reached.
              if (pReq->cbRead != PUSH_BUFSIZE) {
                
                pReq->bReadsDone = TRUE;
                
                // If we read 0 bytes, then the previous data push has been
                // the last one.  So the last data push has already been issued.
                if (pReq->cbRead == 0) {
                  pReq->bLastDataPushIssued = TRUE;
                  
                  // If there are no outstanding pushes, we can do a NULL push.
                  if (pReq->bPushOutstanding == FALSE) {
                    pReq->bNullPush = TRUE;
                  }
                }
              }
              
              pReq->FileOl.Offset += pReq->cbRead;
              
              // We have at most one outstanding push, since RPC runtime
              // guarantees proper notification if there are no more then
              // one push outstanding.
              if (pReq->bPushOutstanding == TRUE)
                Action = Wait;
              else
                Action = Push;
              
              LeaveCriticalSection(&pReq->Lock);
            }
            
          }
          
          //
          // Received pipe IO completion.
          //  
          else if(dwCompletionKey == IoPipe) {
            
#ifdef DEBUG2
            nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: completed Pipe IO for req %p\n"), GetCurrentThreadId(), pReq);
            bufSize -= nCharWritten;
            DbgMsgRecord(Msg);
#endif

            pReq = (SReq *) lpOverlapped;
            
            EnterCriticalSection(&pReq->Lock);


            if (pReq->Async->Event != RpcSendComplete) {
              ASSERT(0);
            }
            
            ASSERT(pReq->bPushOutstanding);
            
            pReq->bPushOutstanding = FALSE;
            
            // The completion of the NULL push terminates the request.
            // The reuest is now handled.
            if (pReq->bNullPush) {
              LeaveCriticalSection(&pReq->Lock);
              ServerShutdownRequest(pReq);
              
              Action = Wait;
            }
            
            // This is a regular data push.
            else {
              
              // If there is a read outstanding, wait for it to complete
              // since we do not have any other data to push at present.
              if (pReq->bReadOutstanding)
                Action = Wait;
              
              // If there is a buffer to send, push it.
              else if (pReq->bBuf)
                Action = Push;
              
              // If the this is the completion of the last data push, then
              // we just need to do the NULL push.
              else if (pReq->bLastDataPushIssued) {
                Action = Push;
                pReq->bNullPush = TRUE;
              }
              
              else {
                /*ASSERT(0);*/Action = Wait;
              }
              
              LeaveCriticalSection(&pReq->Lock);
            }

          }
          
          //
          // Received an internal FileRep IO completion.
          //
          else if(dwCompletionKey == IoFileRep) {
            pReq = (SReq *) lpOverlapped;


#ifdef DEBUG2
            nCharWritten = _stprintf_s(Msg, bufSize, TEXT("THREAD %d: SServiceRequests: completed FileRep internal IO for req %p\n"), GetCurrentThreadId(), pReq);
            bufSize -= nCharWritten;
            DbgMsgRecord(Msg);
#endif
            
            // This is a signal to handle a new request.
            // If the request can be activated, then handle it.
            if(HandleSReq(pReq)) {
              Action = Read;
            }
            // Otherwise, do event.
            else {
              Action = Wait;
            }
          }

          else {
            ASSERT(0);
          }
        }
      }

      else {
        ASSERT(0);
      }
    }
}

void s_RemoteReadAsyncPipe(PRPC_ASYNC_STATE pAsync,
                           handle_t hFileRepClient,
                           TCHAR FileName[128],
                           ASYNC_CHAR_PIPE_TYPE *OutPipe) {
    
    int nReply = 1;

    unsigned long ecount = 0;

    SReq *pReq = NULL;

#ifdef DEBUG2
    TCHAR Msg[MSG_SIZE];
    ULONG bufSize = MSG_SIZE; // Keeps track of remaining size of buffer for _stprintf_s
    int nCharWritten; 
    DbgMsgRecord(TEXT("-> s_RemoteReadAsyncPipe\n"));
#endif

    if((pReq = (SReq *) AutoHeapAlloc(sizeof(SReq))) == NULL) {
        AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));            
        return;
    }

    // Set pReq' fields to NULL so that we will know
    // in ServerShutdownRequest which ones have been initialized.
    pReq->hFileRepClient = hFileRepClient;

    pReq->hTokenHandle = NULL;

    pReq->hLocalFile = NULL;
    pReq->bImpersonating = FALSE;
    pReq->Pri = 0;
    pReq->State = StateArrived;

    pReq->pSID = NULL;

    pReq->Status = 0;

    pReq->Lock.DebugInfo = NULL;

    // The out-pipe we use to pull data from the server.
    pReq->OutPipe = OutPipe;

    // The async handle for the call to RemoteOpen.
    pReq->Async = pAsync;


    pReq->fCallInvalidated = FALSE;

#ifdef DEBUG1
    nServerReqs++;
#endif

    if ((pReq->LocalFileName = (LPTSTR) AutoHeapAlloc((_tcslen(FileName)+1) * sizeof(TCHAR))) == NULL) {
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(ERROR_OUTOFMEMORY, TEXT("RequestFile: AutoHeapAlloc failed"));            
        return;
    }
    CopyMemory(pReq->LocalFileName, FileName, (_tcslen(FileName)+1) * sizeof(TCHAR));

    //
    // Check that we are not exceeding the bound on the number
    // of concurrent requests for this user's priority group.
    // And update the number of such requests.
    //

#ifndef NO_SEC
    RPC_STATUS rpcstatus;

    // Impersonate the caller so that we can get the caller's SID
    if ((rpcstatus = RpcImpersonateClient(pReq->hFileRepClient)) != RPC_S_OK) {      
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(rpcstatus, TEXT("RequestFile: RpcImpersonateClient failed"));
        return;
    }
    pReq->bImpersonating = TRUE;

    // We need to write down the access token for the user so that
    // we can use it for impersonation later when the client binding handle
    // gets deallocated.
    if (OpenThreadToken(GetCurrentThread(),
                        TOKEN_QUERY | TOKEN_IMPERSONATE,
                        TRUE,
                        &pReq->hTokenHandle) == 0) {

        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(GetLastError(), TEXT("RequestFile: OpenThreadToken failed"));
        return;
    }

    // Determine current user's priority level.  This corresponds to the
    // priority level of the callee, since we are impersonating.
    pReq->Pri = GetCurrentUserPriority();

    // Write down the SID for the user.
    pReq->pSID = GetUserSID();

    // Stop impersonating.  We got what we wanted.
    if ((rpcstatus = RpcRevertToSelf()) != RPC_S_OK) {
        pReq->bImpersonating = FALSE;
        
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(rpcstatus, TEXT("RequestFile: RpcRevertToSelf failed"));
        return;
    }
    pReq->bImpersonating = FALSE;

#else

    pReq->Pri = 1;

    unsigned SidLength = GetLengthSid(pAnonSID);
    if ((pReq->pSID = AutoHeapAlloc(SidLength)) == NULL) {
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(GetLastError(), TEXT("RequestFile: AutoHeapAlloc failed"));
        return;
    }

    if (CopySid(SidLength, pReq->pSID, pAnonSID) == 0) {
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddRpcEEInfoAndRaiseException(GetLastError(), TEXT("RequestFile: CopySID failed"));
        return;
    }

#endif

#ifdef DEBUG2
    nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Pri=%d for req %p\n"), pReq->Pri, pReq);
    bufSize -= nCharWritten;
    DbgMsgRecord(Msg);
#endif

    // If there are no workers ot handle the request, create one.

    HANDLE hThread;
    ULONG ThreadIdentifier;
    DWORD status;

    if (nThreadsAtServerCompletionPort < 1) {

      // Go service some requests, remember that you are
      // an RPC thread.
      if ((hThread = CreateThread(NULL,
                                  0,
                                  (LPTHREAD_START_ROUTINE) SServiceRequests,
                                  NULL,
                                  0,
                                  &ThreadIdentifier)) == NULL) {
        
        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        AddToMessageLogProcFailure(TEXT("SServiceRequests: CreateThread"), GetLastError());
        return;
      }
      
      // Unless we close a handle to the thread, it will remain in the
      // system even after its execution has terminated.
      status = CloseHandle(hThread);
      ASSERT(status != 0);  
    }

    // We need to place request onto the queue only after we know that there is someone
    // who will actually be able to pick it up.  Thus, we need to create the thread first.

    // Increment the counter for the number of concurrent requests.
    if (!CounterIncrement(pServerReqCounters[pReq->Pri])) {

        pReq->Status = GetLastError();
        ServerShutdownRequest(pReq);
        // We have to raise exception after deallocating the data
        // since there is a possibility that a client thread
        // will retry and will attempt to open this local file
        // while it's previous request is still holding a lock.
        // This has been actually hit on an overloaded system.
        AddRpcEEInfoAndRaiseException(RPC_S_SERVER_TOO_BUSY, TEXT("RequestFile: CounterIncrement failed"));
        return;
    }
#ifdef DEBUG2
    nCharWritten = _stprintf_s(Msg, bufSize, TEXT("Incremented ServerReqCounters[%d]\n"), pReq->Pri);
    bufSize -= nCharWritten;
    DbgMsgRecord(Msg);
#endif            
    
    pReq->State = StateQueued;

#ifdef DEBUG2
    nCharWritten = _stprintf_s(Msg, bufSize, TEXT("RequestFile: Put req %p onto Req queue %p\n"), pReq, ServerReqQueues[pReq->Pri]);
    bufSize -= nCharWritten;
    DbgMsgRecord(Msg);
#endif    

    // Place the request onto the queue.
    QueueAdd(ServerReqQueues[pReq->Pri], pReq, TRUE);

    // After this point the request can be deleted.  Do not touch it.

    // Now if we can, find a queued request and activate it.
    FindAndActivateSReq();

#ifdef DEBUG2
    DbgMsgRecord(TEXT("<- RequestFile\n"));
#endif

    return;
}

// end FileRepServerProc.cpp


